﻿#region Imports
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using BulkRenamer.Util.Enums;
using Microsoft.VisualBasic;
#endregion

namespace BulkRenamer.Util.Extensions
{
    /// <summary>
    /// Extension methods for collections
    /// </summary>
    /// <seealso cref="http://michlg.wordpress.com/category/programming/net/wpf/" />
    public static class CollectionExtensions
    {
        /// <summary>
        /// Evaluates a collection if it is iterable through Linq
        /// </summary>
        /// <typeparam name="T">Type of value in collection</typeparam>
        /// <param name="value">Collection to evaluate</param>
        /// <param name="checkCount">TRUE: checks count > 0</param>
        /// <returns></returns>
        public static bool IsLinqIterable<T>(this IEnumerable<T> value, bool checkCount)
        {
            return !Utilities.IsNull(value) ? (checkCount ? value.Count() > 0 : true) : false;
        }

        public static IEnumerable<TSource> AddItem<TSource>(this IEnumerable<TSource> source, TSource value)
            where TSource : class
        {
            ObservableCollection<TSource> res = new ObservableCollection<TSource>(source);
            res.Add(value);
            return res.AsEnumerable();

            //ObservableCollection<TSource> res = new ObservableCollection<TSource>(source);
            //res.Add(value);
            //source = new ObservableCollection<TSource>(res);
            //return source.AsEnumerable();

            //source.ToList().Add(value);
            //return source.AsEnumerable();
        }

        public static IEnumerable<TSource> AddItems<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> values)
            where TSource : class
        {
            //values.GetEnumerator().Reset();
            //while (values.GetEnumerator().MoveNext())
            //    source = source.AddNewItem(values.GetEnumerator().Current);

            foreach (TSource val in values) source = source.AddItem(val);
            return source;
        }

        public static IEnumerable<TSource> RemoveItem<TSource>(this IEnumerable<TSource> source, TSource value)
            where TSource : class
        {
            ObservableCollection<TSource> res = new ObservableCollection<TSource>(source);
            if (res.Contains(value)) res.Remove(value);
            return res.AsEnumerable();
        }

        public static IEnumerable<TSource> RemoveItems<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> values)
            where TSource : class
        {
            //values.GetEnumerator().Reset();
            //while (values.GetEnumerator().MoveNext())
            //    source = source.RemoveItem(values.GetEnumerator().Current);

            ObservableCollection<TSource> res = new ObservableCollection<TSource>(source);
            foreach (TSource val in values) res = res.RemoveItem(val).ToObservableCollection();
            return res.AsEnumerable();
        }

        public static IEnumerable<TSource> BeforeItems<TSource>(this IEnumerable<TSource> source, TSource value, bool inclSpecifiedItem)
            where TSource : class
        {
            ObservableCollection<TSource> res = new ObservableCollection<TSource>(source);
            if (res.Contains(value))
            {
                int i = res.IndexOf(value);
                res = new ObservableCollection<TSource>(res.Where(itm => inclSpecifiedItem ? res.IndexOf(itm) <= i : res.IndexOf(itm) < i));
            }

            return res.AsEnumerable();
        }

        public static IEnumerable<TSource> AfterItems<TSource>(this IEnumerable<TSource> source, TSource value, bool inclSpecifiedItem)
            where TSource : class
        {
            ObservableCollection<TSource> res = new ObservableCollection<TSource>(source);
            if (res.Contains(value))
            {
                int i = res.IndexOf(value);
                res = new ObservableCollection<TSource>(res.Where(itm => inclSpecifiedItem ? res.IndexOf(itm) >= i : res.IndexOf(itm) > i));
            }

            return res.AsEnumerable();
        }

        public static IEnumerable<TSource> SkipTill<TSource>(this IEnumerable<TSource> source, TSource value, bool inclSpecifiedItem)
            where TSource : class
        {
            ObservableCollection<TSource> res = new ObservableCollection<TSource>(source);
            if (res.Contains(value))
            {
                int i = res.IndexOf(value);
                //res = new ObservableCollection<TSource>(res.Where(itm => inclSpecifiedItem ? res.IndexOf(itm) >= i : res.IndexOf(itm) > i));
                res = new ObservableCollection<TSource>(res.Skip(inclSpecifiedItem ? i + 1 : i));
            }

            return res.AsEnumerable();
        }

        // Summary:
        //     Filters a sequence of values based on a predicate. Each element's index is
        //     used in the logic of the predicate function.
        //
        // Parameters:
        //   source:
        //     An System.Collections.Generic.IEnumerable<TSource> to filter.
        //
        //   predicate:
        //     A function to test each source element for a condition; the second parameter
        //     of the function represents the index of the source element.
        //
        // Type parameters:
        //   TSource:
        //     The type of the elements of source.
        //
        // Returns:
        //     An System.Collections.Generic.IEnumerable<TSource> that contains elements from
        //     the input sequence that satisfy the condition.
        //
        // Exceptions:
        //   System.ArgumentNullException:
        //     source or predicate is null.
        public static IEnumerable<TSource> SkipWhere<TSource>(this IEnumerable<TSource> source, Func<TSource, int, bool> predicate)
        {// RKD
            return source.Where(predicate).AsEnumerable();
        }

        // Summary:
        //     Filters a sequence of values based on a predicate.
        //
        // Parameters:
        //   source:
        //     An System.Collections.Generic.IEnumerable<TSource> to filter.
        //
        //   predicate:
        //     A function to test each element for a condition.
        //
        // Type parameters:
        //   TSource:
        //     The type of the elements of source.
        //
        // Returns:
        //     An System.Collections.Generic.IEnumerable<TSource> that contains elements from
        //     the input sequence that satisfy the condition.
        //
        // Exceptions:
        //   System.ArgumentNullException:
        //     source or predicate is null.
        public static IEnumerable<TSource> SkipWhere<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
        {// RKD
            return source.Where(predicate).AsEnumerable();
        }

        public static IEnumerable<TSource> SkipWhereNot<TSource>(this IEnumerable<TSource> source, Func<TSource, int, bool> predicate)
        {// RKD
            return source.Where(src => !source.Where(predicate).Contains(src)).AsEnumerable();
        }

        public static IEnumerable<TSource> SkipWhereNot<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
        {// RKD
            return source.Where(src => !source.Where(predicate).Contains(src)).AsEnumerable();
        }

        public static bool HasCount<TSource>(this IEnumerable<TSource> source, long count, eComparers comparers = eComparers.Equals)
        {
            if (source.IsLinqIterable<TSource>(false))
            {
                switch (comparers)
                {
                    case eComparers.GreaterThan:
                        return source.LongCount() > count;
                    case eComparers.LesserThan:
                        return source.LongCount() < count;
                    case eComparers.GreaterThanOrEquals:
                        return source.LongCount() >= count;
                    case eComparers.LesserThanOrEquals:
                        return source.LongCount() <= count;
                    case eComparers.NotEquals:
                        return source.LongCount() != count;
                    case eComparers.NotGreaterThan:
                        return !(source.LongCount() > count);
                    case eComparers.NotLesserThan:
                        return !(source.LongCount() < count);
                    case eComparers.NotGreaterThanOrEquals:
                        return !(source.LongCount() >= count);
                    case eComparers.NotLesserThanOrEquals:
                        return !(source.LongCount() <= count);
                    case eComparers.Equals:
                        return source.LongCount() == count;
                    default:
                        return false;
                }
            }
            else
                return false;
        }

        public static IEnumerable<TSource> AddCollectionIndex<TSource>(this IEnumerable<TSource> source, string propName)
        {
            if (Utilities.HasProperty<TSource>(propName, false))
                source.ToList().ConvertAll(itm => itm = Utilities.SetValue(itm, propName, source.ToList().IndexOf(itm) + 1));

            return source.AsEnumerable();
        }

        public static ObservableCollection<TSource> ToObservableCollection<TSource>(this IEnumerable<TSource> source)
        {
            return new ObservableCollection<TSource>(source);
        }

        /// <summary>
        /// Performs the specified action on each element of the System.Collections.Generic.IEnumerable<TSource>
        /// </summary>
        /// <typeparam name="TSource">Type of data</typeparam>
        /// <param name="source">Collection to iterate through</param>
        /// <param name="action">The System.Action<TSource> delegate to perform on each element of the System.Collections.Generic.IEnumerable<TSource>/param>
        /// <example>System.ArgumentNullException: action is null</example>
        public static void ForEach<TSource>(this IEnumerable<TSource> source, Action<TSource> action)
        {
            source.ToList().ForEach(action);
        }

        public static IEnumerable<TSource> ConvertAll<TSource>(this IEnumerable<TSource> source, Converter<object, TSource> converter)
        {
            source.ToList().ConvertAll(converter);
            return source.AsEnumerable();
        }







        //using (IdNameValueMember idv = new IdNameValueMember())
        //{
        //    List<Expression<Func<IdNameValueMember, bool>>> whrecls = new List<Expression<Func<IdNameValueMember, bool>>>();
        //    whrecls.Add(z => z.Id.ToString() == z.Name.ToString());
        //    whrecls.Add(z => z.Id.GetType() == z.Name.GetType());

        //    List<IdNameValueMember> op = new List<IdNameValueMember>();
        //    op = op.Select(whrecls).ToList();
        //}

        /*
        /// <summary>
        /// This method returns the current item of the given collection.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the collection.</typeparam>
        /// <param name="source">The collection.</param>
        public static TSource GetCurrentItem<TSource>(this IEnumerable<TSource> source)
        {
            if (source == null)
                return default(TSource);
            var collectionView = CollectionViewSource.GetDefaultView(source);
            return (TSource)collectionView.CurrentItem;
        }

        /// <summary>
        /// This method returns the current position within the given collection.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the collection.</typeparam>
        /// <param name="source">The collection.</param>
        public static int GetCurrentPosition<TSource>(this IEnumerable<TSource> source)
        {
            if (source == null)
                return -1;
            var collectionView = CollectionViewSource.GetDefaultView(source);
            return collectionView.CurrentPosition;
        }

        /// <summary>
        /// This method sets the current item of the given collection.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the collection.</typeparam>
        /// <param name="source">The collection.</param>
        /// <param name="item">The item which should be set as the current one.</param>
        public static void SetCurrentItem<TSource>(this IEnumerable<TSource> source, TSource item)
        {
            if (source == null) return;
            var collectionView = CollectionViewSource.GetDefaultView(source);
            collectionView.MoveCurrentTo(item);
        }

        /// <summary>
        /// This method moves the current item to the first.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the collection.</typeparam>
        /// <param name="source">The collection.</param>
        public static void MoveCurrentToFirst<TSource>(this IEnumerable<TSource> source)
        {
            if (source == null) return;
            var collectionView = CollectionViewSource.GetDefaultView(source);
            collectionView.MoveCurrentToFirst();
        }

        /// <summary>
        /// This method moves the current item to the previous.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the collection.</typeparam>
        /// <param name="source">The collection.</param>
        public static void MoveCurrentToPrevious<TSource>(this IEnumerable<TSource> source)
        {
            if (source == null) return;
            var collectionView = CollectionViewSource.GetDefaultView(source);
            collectionView.MoveCurrentToPrevious();
        }

        /// <summary>
        /// This method moves the current item to the next.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the collection.</typeparam>
        /// <param name="source">The collection.</param>
        public static void MoveCurrentToNext<TSource>(this IEnumerable<TSource> source)
        {
            if (source == null) return;
            var collectionView = CollectionViewSource.GetDefaultView(source);
            collectionView.MoveCurrentToNext();
        }

        /// <summary>
        /// This method moves the current item to the last.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the collection.</typeparam>
        /// <param name="source">The collection.</param>
        public static void MoveCurrentToLast<TSource>(this IEnumerable<TSource> source)
        {
            if (source == null) return;
            var collectionView = CollectionViewSource.GetDefaultView(source);
            collectionView.MoveCurrentToLast();
        }

        /// <summary>
        /// This method sets the current position withing the given collection
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the collection</typeparam>
        /// <param name="source">The collection</param>
        /// <param name="index">The new position</param>
        public static void SetCurrentPosition<TSource>(this IEnumerable<TSource> source, int index)
        {
            if (source == null) return;
            var collectionView = CollectionViewSource.GetDefaultView(source);
            collectionView.MoveCurrentToPosition(index);
        }

        /// <summary>
        /// Removes the items from the collection according to the predicate.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="predicate">The predicate.</param>
        public static void Remove<TSource>(this ICollection<TSource> source, Func<TSource, bool> predicate)
        {
            if (source == null) return;
            var itemsToRemove = source.Where(predicate).ToList();
            foreach (var item in itemsToRemove)
                source.Remove(item);
        }
    */

        #region Methods
        public static string ToString()
        {
            return Utilities.ToString(typeof(CollectionExtensions));
        }
        #endregion
    }
}
